package com.liaoyifan.core.opera;

import com.liaoyifan.core.enums.CoreEnum;
import com.liaoyifan.core.valid.Lang;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.extern.log4j.Log4j2;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;

@SuppressWarnings({"unused", "unchecked"})
@Log4j2
public class CoreEnumConverterFactory implements ConverterFactory<String, CoreEnum<?>> {

    private static final Map<Class<? extends CoreEnum<?>>, Map<String, CoreEnum<?>>> ENUM_CACHE =
            new ConcurrentHashMap<>();

    @SuppressWarnings("NullableProblems")
    @Override
    public <T extends CoreEnum<?>> Converter<String, T> getConverter(Class<T> targetType) {
        return source -> {
            if (Lang.isBlank(source)) {
                return null;
            }
            Map<String, T> valueMap = (Map<String, T>) getOrCreateValueMap(targetType);
            T enumValue = valueMap.get(source);
            if (enumValue == null) {
                log.warn(() -> "No matching enum found for value: " + source);
                throw new IllegalArgumentException();
            }
            return enumValue;
        };
    }

    private <T extends CoreEnum<?>> Map<String, CoreEnum<?>> getOrCreateValueMap(
            Class<T> enumType) {
        return ENUM_CACHE.computeIfAbsent(
                enumType,
                k -> {
                    T[] enumConstants = enumType.getEnumConstants();
                    if (enumConstants == null || enumConstants.length == 0) {
                        log.warn(() -> "Empty enum: " + enumType.getName());
                        throw new IllegalStateException();
                    }
                    return Arrays.stream(enumConstants)
                            .collect(
                                    Collectors.toConcurrentMap(
                                            e -> String.valueOf(e.getValue()),
                                            Function.identity(),
                                            (existing, replacement) -> {
                                                log.warn(
                                                        () ->
                                                                "Enum value conflict: "
                                                                        + existing.getValue()
                                                                        + " in "
                                                                        + enumType.getName());
                                                throw new IllegalArgumentException();
                                            },
                                            ConcurrentHashMap::new));
                });
    }
}
